package com.sugon.modules.analyse.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.sugon.dao.*;
import com.sugon.entity.ModuleArchivesEntity;
import com.sugon.entity.ModuleSpPurchasingBatchEntity;
import com.sugon.entity.ModuleTypeEntity;
import com.sugon.entity.MoldTypePositionEntity;
import com.sugon.modules.analyse.model.chart.EchartsSeriesItemModel;
import com.sugon.modules.analyse.model.chart.LineChartDataModel;
import com.sugon.modules.analyse.model.dto.MoldReplacePartFeeItemDTO;
import com.sugon.modules.analyse.model.dto.MoldTimesMonthlyStatItemDTO;
import com.sugon.modules.analyse.model.vo.MoldSingleCostStatVO;
import com.sugon.modules.analyse.model.vo.MoldTypeCostStatVO;
import com.sugon.modules.analyse.service.IMoldCostAnalyseService;
import com.sugon.modules.utils.DateUtil;
import com.sugon.service.ModuleArchivesService;
import lombok.Data;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author YuXD
 * @Version 1.0
 * @since 2023/2/27
 */
@Service
public class MoldCostAnalyseServiceImpl implements IMoldCostAnalyseService {

    @Resource
    private ModuleTypeDao moldTypeDao;
    @Resource
    private ModuleTypePositionDao moldTypePosDao;
    @Resource
    private ModuleSpPurchasingBatchDao moldSpPurchasingBatchDao;
    @Resource
    private ModuleArchivesService moldService;
    @Resource
    private ModuleReplacePartDao moldReplacePartDao;
    @Resource
    private ModuleTimesRecordProdDao moldTimesDao;

    @Override
    public List<MoldTypeCostStatVO> doMoldTypeCostStat(String yearly, String moldTypeId) {
        List<MoldTypeCostStatVO> result = CollectionUtil.newArrayList();
        // 获取模具类型&每类模具下的位置列表
        List<ModuleTypeEntity> moldTypeList = moldTypeDao.loadMoldTypePositionTree(moldTypeId);
        // 获取yearly年度型芯的采购价格信息，用来计算理论单台成本
        Map<String, MoldSpYearlyPurchasingPriceStat> moldSpYearlyPurchasingMap = loadMoldSpYearlyPurchasing(yearly, null);
        // TODO 获取yearly年度型芯的采购价格信息，用来计算实际单台成本


        MoldTypeCostStatVO costStatVO;
        for (ModuleTypeEntity moldType : moldTypeList) {
            costStatVO = new MoldTypeCostStatVO(moldType.getId(), moldType.getTypeName());
            // 设置单台组成数
            List<MoldTypePositionEntity> positionList = moldType.getPositionList();
            if (CollectionUtil.isNotEmpty(positionList)) {
                costStatVO.setNum(positionList.size());
                // 设置模具理论单台成本
                BigDecimal theorySingleCost = BigDecimal.ZERO;
                for (MoldTypePositionEntity position : positionList) {
                    // 根据当前位置设置的条码代码 -> 找到型芯 -> 型芯的月度价格信息，取平均值
                    String barCodeRange = position.getBarCodeRange();
                    if (StrUtil.isEmpty(barCodeRange)) {
                        continue;
                    }
                    MoldSpYearlyPurchasingPriceStat yearlyPurchasingPriceStat = moldSpYearlyPurchasingMap.get(barCodeRange);
                    if (yearlyPurchasingPriceStat == null) {
                        continue;
                    }
                    /*
                        计算模具分类-理论单台成本：
                        当前分类下每个位置的理论单台 = 这个位置的单价/这个位置的寿命标准
                        模具分类-理论单台 = 当前分类下的所有位置理论单台相加
                     */
                    // 设置单价
                    position.setUnitPrice(yearlyPurchasingPriceStat.getUnitPrice());
                    // 设置理论单台 = 单价÷寿命标准
                    position.setTheorySingleCost(NumberUtil.div(position.getUnitPrice(), position.getLifeStandard(), 5));
                    // 模具分类-单台成本 = 各个位置下的理论单台成本相加
                    theorySingleCost = NumberUtil.add(theorySingleCost, position.getTheorySingleCost());
                    /*
                        计算实际单台：
                     */

                }
                // 设置理论单台成本
                costStatVO.setTheorySingleCost(theorySingleCost);
            }
            result.add(costStatVO);
        }
        return result;
    }

    @Override
    public List<MoldSingleCostStatVO> doMoldCostStat(String yearly, String moldTypeId) {
        List<MoldSingleCostStatVO> moldSingleCostStatList = CollectionUtil.newArrayList();
        // 获取指定类型的模具列表
        List<ModuleArchivesEntity> moldList = moldService.queryList(moldTypeId);
        if (CollectionUtil.isEmpty(moldList)) {
            return moldSingleCostStatList;
        }
        List<String> moldIdList = moldList.stream().map(ModuleArchivesEntity::getId).collect(Collectors.toList());
        // 获取模具单台成本目标值，以模具类型ID为键，该类型的理论成本为值
        List<MoldTypeCostStatVO> moldTypeCostStatList = doMoldTypeCostStat(yearly, moldTypeId);
        Map<String, BigDecimal> collect = moldTypeCostStatList.stream().collect(Collectors.toMap(MoldTypeCostStatVO::getMoldTypeId, MoldTypeCostStatVO::getTheorySingleCost));
        // 获取当前年度下的维修换件费用汇总
        Map<String, List<MoldReplacePartFeeItemDTO>> moldRepairMonthlyFeeStatMap = getRepairFeeMonthlyStat(yearly, moldIdList);
        // 获取当前年度下的模次汇总
        Map<String, List<MoldTimesMonthlyStatItemDTO>> moldTimesMonthlyStatMap = getMoldTimesMonthlyStat(yearly, moldIdList);
        // 获取本年度的各个月份信息
        List<String> yearMonths = DateUtil.getYearMonths(yearly);
        // 声明模具单台变量
        MoldSingleCostStatVO moldSingle;
        for (ModuleArchivesEntity mold : moldList) {
            moldSingle = new MoldSingleCostStatVO(mold.getModuleNo(), mold.getModuleTypeName());
            // 设置单台目标值
            BigDecimal targetCost = collect.get(mold.getModuleType());
            if (targetCost != null) {
                moldSingle.setTheorySingleCost(targetCost);
            }
            // 获取当前模具本年度下各月份的模次汇总数据
            List<MoldTimesMonthlyStatItemDTO> monthlyMoldTimesList = moldTimesMonthlyStatMap.get(mold.getId());
            if (monthlyMoldTimesList == null) {
                monthlyMoldTimesList = CollectionUtil.newArrayList();
            }
            Map<String, List<MoldTimesMonthlyStatItemDTO>> monthlyMoldTimesMap = monthlyMoldTimesList.stream().collect(Collectors.groupingBy(MoldTimesMonthlyStatItemDTO::getMonthly));

            // 获取当前模具本年度下各月份的维修换件费用数据
            List<MoldReplacePartFeeItemDTO> monthlyRepairFeeList = moldRepairMonthlyFeeStatMap.get(mold.getId());
            if (monthlyRepairFeeList == null) {
                monthlyRepairFeeList = CollectionUtil.newArrayList();
            }
            Map<String, List<MoldReplacePartFeeItemDTO>> monthlyFeeMap = monthlyRepairFeeList.stream().collect(Collectors.groupingBy(MoldReplacePartFeeItemDTO::getMonthly));

            BigDecimal yearlyFee = BigDecimal.ZERO;
            BigDecimal yearlyMoldTimes = BigDecimal.ZERO;
            // 设置当前模具各月份单台成本
            for (int i = 0; i < yearMonths.size(); i++) {
                // 获取某副模具在某一个月份下是否有模次数据。如果没有则继续判断下一个月份
                List<MoldTimesMonthlyStatItemDTO> moldTimesMonthlyStatItemDTOS = monthlyMoldTimesMap.get(yearMonths.get(i));
                if (CollectionUtil.isEmpty(moldTimesMonthlyStatItemDTOS)) {
                    continue;
                }
                // 获取月度模次数
                Integer monthlyMoldTimes = moldTimesMonthlyStatItemDTOS.get(0).getTotalMoldTimes();
                // 计算年度模次
                yearlyMoldTimes = NumberUtil.add(yearlyMoldTimes, monthlyMoldTimes);
                // 获取某副模具在某一个月份下是否有维修换件。如果没有则继续判断下一个月份
                List<MoldReplacePartFeeItemDTO> monthlyFees = monthlyFeeMap.get(yearMonths.get(i));
                if (CollectionUtil.isEmpty(monthlyFees)) {
                    continue;
                }
                // 获取月度维修费用
                BigDecimal monthlyFee = monthlyFees.get(0).getMonthlyFee();
                // 累加年度维修费用、模次数，以便后面进行计算
                yearlyFee = NumberUtil.add(yearlyFee, monthlyFee);
                // 设置某副模具指定月份下的模具单台
                ReflectUtil.setFieldValue(moldSingle, "cost" + (i + 1), NumberUtil.div(monthlyFee, monthlyMoldTimes, 5));
            }
            // 设置该幅模具 年度单台成本
            if (!NumberUtil.equals(yearlyMoldTimes, BigDecimal.ZERO)) {
                moldSingle.setYearlyCost(NumberUtil.div(yearlyFee, yearlyMoldTimes, 5));
            }
            // 添加到集合中
            moldSingleCostStatList.add(moldSingle);
        }
        return moldSingleCostStatList;
    }

    @Override
    public LineChartDataModel doMoldCostLineStat(String yearly, String moldType) {
        LineChartDataModel lineChartData = new LineChartDataModel(yearly + "年模具单台明细变化趋势");
        // 设置横坐标月份显示
        lineChartData.setXAxisData(CollectionUtil.newArrayList("1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"));

        // 获取模具单台年度成本统计
        List<MoldSingleCostStatVO> moldSingleCostStatList = doMoldCostStat(yearly, moldType);
        if (CollectionUtil.isEmpty(moldSingleCostStatList)) {
            return lineChartData;
        }

        // 设置legendData
        lineChartData.setLegendData(moldSingleCostStatList.stream().map(MoldSingleCostStatVO::getMoldNo).collect(Collectors.toList()));
        // 先设置每副模具的数据
        for (MoldSingleCostStatVO moldSingleCostStat : moldSingleCostStatList) {
            EchartsSeriesItemModel seriesData = new EchartsSeriesItemModel(moldSingleCostStat.getMoldNo(), "line", "Total");
            seriesData.setData(CollectionUtil.newArrayList(moldSingleCostStat.getCost1(), moldSingleCostStat.getCost2(), moldSingleCostStat.getCost3(), moldSingleCostStat.getCost4(), moldSingleCostStat.getCost5(), moldSingleCostStat.getCost6(), moldSingleCostStat.getCost7(), moldSingleCostStat.getCost8(), moldSingleCostStat.getCost9(), moldSingleCostStat.getCost10(), moldSingleCostStat.getCost11(), moldSingleCostStat.getCost12()));
            lineChartData.addSeriesData(seriesData);
        }
        // 设置目标值/理论值
        EchartsSeriesItemModel targetSeriesData = new EchartsSeriesItemModel("目标值", "line", "Total");
        // 目标值 = 理论值总和 然后求个平均
        BigDecimal totalTheorySingleCost = NumberUtil.toBigDecimal(moldSingleCostStatList.stream().mapToDouble(singleCost -> singleCost.getTheorySingleCost().doubleValue()).sum());
        BigDecimal targetValue = NumberUtil.div(totalTheorySingleCost, moldSingleCostStatList.size(), 5);
        targetSeriesData.setData(CollectionUtil.newArrayList(targetValue, targetValue, targetValue, targetValue, targetValue, targetValue, targetValue, targetValue, targetValue, targetValue, targetValue, targetValue));
        lineChartData.addSeriesData(targetSeriesData);

        // 设置年度值
        EchartsSeriesItemModel yearlySeriesData = new EchartsSeriesItemModel("年度值", "line", "Total");
        // 目标值 = 年度单台总和 然后求个平均
        BigDecimal totalYearlySingleCost = NumberUtil.toBigDecimal(moldSingleCostStatList.stream().mapToDouble(singleCost -> singleCost.getYearlyCost().doubleValue()).sum());
        BigDecimal yearlyValue = NumberUtil.div(totalYearlySingleCost, moldSingleCostStatList.size(), 5);
        yearlySeriesData.setData(CollectionUtil.newArrayList(yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue, yearlyValue));
        lineChartData.addSeriesData(yearlySeriesData);

        return lineChartData;
    }

    /**
     * 获取指定模具列表范围内，指定年度下各月度的模次汇总
     *
     * @param yearly
     * @param moldIdList
     * @return
     */
    private Map<String, List<MoldTimesMonthlyStatItemDTO>> getMoldTimesMonthlyStat(String yearly, List<String> moldIdList) {
        Map<String, List<MoldTimesMonthlyStatItemDTO>> moldTimesMonthlyStat = MapUtil.newHashMap();
        List<MoldTimesMonthlyStatItemDTO> moldTimesMonthlyStatItems = moldTimesDao.doMoldTimesMonthlyStat(yearly, moldIdList);
        if (CollectionUtil.isNotEmpty(moldTimesMonthlyStatItems)) {
            moldTimesMonthlyStat = moldTimesMonthlyStatItems.stream().collect(Collectors.groupingBy(MoldTimesMonthlyStatItemDTO::getMoldId));
        }
        return moldTimesMonthlyStat;
    }

    /**
     * 获取指定模具列表范围内，指定年度下各月度的维修费用汇总
     *
     * @param yearly
     * @param moldIdList
     * @return
     */
    private Map<String, List<MoldReplacePartFeeItemDTO>> getRepairFeeMonthlyStat(String yearly, List<String> moldIdList) {
        // 获取各模具当前年度下 不同月份下的模具维修费用汇总
        Map<String, List<MoldReplacePartFeeItemDTO>> moldRepairMonthlyFeeStatMap = MapUtil.newHashMap();
        List<MoldReplacePartFeeItemDTO> moldRepairMonthlyFeeStats = moldReplacePartDao.doMoldRepairFeeStat(yearly, moldIdList);
        if (CollectionUtil.isNotEmpty(moldRepairMonthlyFeeStats)) {
            moldRepairMonthlyFeeStatMap = moldRepairMonthlyFeeStats.stream().collect(Collectors.groupingBy(MoldReplacePartFeeItemDTO::getMoldId));
        }
        return moldRepairMonthlyFeeStatMap;
    }

    @Override
    public List<MoldTypePositionEntity> getPositionCostStatItem(String moldTypeId, String yearly) {

        // 获取模具类型位置列表
        List<MoldTypePositionEntity> positionList = moldTypePosDao.loadPositionList(moldTypeId);

        // 获取模具型芯年度采购记录MAP
        Map<String, MoldSpYearlyPurchasingPriceStat> moldSpYearlyPurchasingMap = loadMoldSpYearlyPurchasing(yearly, positionList.stream().map(MoldTypePositionEntity::getBarCodeRange).collect(Collectors.toList()));
        for (MoldTypePositionEntity moldTypePosition : positionList) {
            String barCodeRange = moldTypePosition.getBarCodeRange();
            if (StrUtil.isEmpty(barCodeRange)) {
                continue;
            }
            MoldSpYearlyPurchasingPriceStat yearlyPurchasingPriceStat = moldSpYearlyPurchasingMap.get(barCodeRange);
            if (yearlyPurchasingPriceStat == null) {
                continue;
            }
            // 设置单价
            moldTypePosition.setUnitPrice(yearlyPurchasingPriceStat.getUnitPrice());
            // 设置理论单台 = 单价÷寿命标准
            moldTypePosition.setTheorySingleCost(NumberUtil.div(moldTypePosition.getUnitPrice(), moldTypePosition.getLifeStandard(), 5));
        }
        return positionList;
    }

    /**
     * 计算月度单价：
     * 极限一点：比如每个月都有自己不同的单价，那么计算平均值
     *
     * @param moldSpPurchasingBatchMap 模具型芯采购信息
     * @param barCodeRange             适配型芯条码代码规则， 比如0634 或 0001|0003 两种格式
     * @return
     */
    private BigDecimal computeMonthlyUnitPrice(Map<String, List<MoldSpMonthlyPurchasingPrice>> moldSpPurchasingBatchMap, String barCodeRange) {
        // 适配型芯条码代码规则过滤出 具体型芯的采购价格记录
        Map<String, List<MoldSpMonthlyPurchasingPrice>> filteredPurchasing = MapUtil.filter(moldSpPurchasingBatchMap, entry -> StrUtil.contains(barCodeRange, entry.getKey()));
        if (MapUtil.isEmpty(filteredPurchasing)) {
            return BigDecimal.ZERO;
        }
        Collection<List<MoldSpMonthlyPurchasingPrice>> values = filteredPurchasing.values();
        Double totalPrice = 0.0;
        int monthNum = 0;
        for (List<MoldSpMonthlyPurchasingPrice> value : values) {
            monthNum += value.size();
            totalPrice += value.stream().mapToDouble(MoldSpMonthlyPurchasingPrice::getPurchasingPrice).sum();
        }
        return NumberUtil.div(NumberUtil.toBigDecimal(totalPrice), monthNum);
    }

    /**
     * 获取模具型芯年度采购价格信息
     * 返回一个map类型，key：模具备件的条码代码 value:每个月份的价格
     *
     * @param yearly
     * @return
     */
    private Map<String, MoldSpYearlyPurchasingPriceStat> loadMoldSpYearlyPurchasing(String yearly, List<String> uniqueBarCodeList) {
        Map<String, MoldSpYearlyPurchasingPriceStat> tmp = MapUtil.newHashMap();
        // 获取指定年度下所有的模具型芯采购信息
        List<ModuleSpPurchasingBatchEntity> yearlyPurchasingList = moldSpPurchasingBatchDao.findMatchedYearlyPurchasingBatch(yearly, uniqueBarCodeList);
        if (CollectionUtil.isEmpty(yearlyPurchasingList)) {
            return tmp;
        }
        // 获取已模具型芯条码代码的分组
        Map<String, List<ModuleSpPurchasingBatchEntity>> moldSpGroup = yearlyPurchasingList.stream().collect(Collectors.groupingBy(ModuleSpPurchasingBatchEntity::getUniqueBarCode));
        moldSpGroup.forEach((uniqueBarCode, batchList) -> {
            MoldSpYearlyPurchasingPriceStat yearlyPriceStat = new MoldSpYearlyPurchasingPriceStat(yearly);
            // 从采购批量列表中    排除掉
            List<MoldSpMonthlyPurchasingPrice> monthlyPurchasingList = CollectionUtil.newArrayList();
            for (ModuleSpPurchasingBatchEntity batch : batchList) {
                // 获取月份列表，过滤掉不是本年度的
                List<String> monthlyList = batch.getMonthlyList();
                if (CollectionUtil.isEmpty(monthlyList)) {
                    continue;
                }
                monthlyList = monthlyList.stream().filter(monthly -> StrUtil.containsAny(monthly, yearly)).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(monthlyList)) {
                    for (String monthly : monthlyList) {
                        monthlyPurchasingList.add(new MoldSpMonthlyPurchasingPrice(monthly, batch.getUnitPrice().doubleValue()));
                    }
                }
            }
            // 设置当前型芯各月份下的采购价格
            yearlyPriceStat.setMonthlyPurchasingPrices(monthlyPurchasingList);

            tmp.put(uniqueBarCode, yearlyPriceStat);
        });
        return tmp;
    }

    @Data
    private class MoldSpMonthlyPurchasingPrice {

        /**
         * 月份，格式为：yyyyMM
         */
        private String monthly;

        /**
         * 指定月份的采购价格
         */
        private Double purchasingPrice;

        public MoldSpMonthlyPurchasingPrice(String monthly, Double purchasingPrice) {
            this.monthly = monthly;
            this.purchasingPrice = purchasingPrice;
        }

    }

    @Data
    private class MoldSpYearlyPurchasingPriceStat {

        /**
         * 年度，格式为：yyyy
         */
        private String yearly;

        /**
         * 整个年度下的月度单价
         * 从年度下各月份求平均值
         */
        private BigDecimal unitPrice;

        /**
         * 本年度各个月份下的采购价格统计
         */
        private List<MoldSpMonthlyPurchasingPrice> monthlyPurchasingPrices;

        public MoldSpYearlyPurchasingPriceStat(String yearly) {
            this.yearly = yearly;
            this.unitPrice = BigDecimal.ZERO;
        }

        public BigDecimal getUnitPrice() {
            BigDecimal unitPrice = BigDecimal.ZERO;
            if (CollectionUtil.isNotEmpty(this.monthlyPurchasingPrices)) {
                // 设置理论单台
                double totalPurchasingPrice = this.monthlyPurchasingPrices.stream().mapToDouble(MoldSpMonthlyPurchasingPrice::getPurchasingPrice).sum();
                int monthlyNum = this.monthlyPurchasingPrices.size();
                if (monthlyNum > 0) {
                    unitPrice = NumberUtil.div(NumberUtil.toBigDecimal(totalPurchasingPrice), monthlyNum, 2);
                }
            }
            return unitPrice;
        }
    }

}
